﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace JWalsh.XnaSamples.Framework
{
    public class FpsCamera
    {
        private Matrix _view;
        private Matrix projection;
        private float _leftrightRot;
        private float _updownRot;
        private float _rotationSpeed = 0.005f;
        private float _sensibility = 0.5f;
        private float _moveSpeed = 10f;
        private const float FieldOdView = MathHelper.PiOver4;
        private const float AspectRatio = 4f/3f;
        private const float NearPlane = 1;
        private const float FarPlane = 10000;
        private Vector3 _position;
        private Vector3 _target;
        private Vector3 _up;
        int _width;
        int _height;
        private MouseState _originalMouseState;
        public float AvatarYaw;
        public float AvatarXaw;
        public Vector3 PlayerPosition = new Vector3(0, 0, 0);
        Vector3 _playerHeadOffset = new Vector3(0, 10, 0);

        Vector3 _cameraReference = new Vector3(0, 0, 10);
        Vector3 _thirdPersonReference = new Vector3(0, 500, -500);

        public int _cameraState;
        private bool cameraStateKeyDown;
        private bool thisdPerson;

        public FpsCamera(Viewport viewport, Vector3 position, Vector3 up, float updownAngle = 0, float leftrightAngle = 0)
        {
            this._position = position;
            this._leftrightRot = leftrightAngle;
            this._updownRot = updownAngle;
            this._up = up;
            this._width = viewport.Width;
            this._height = viewport.Height;
            Mouse.SetPosition(viewport.Width / 2, viewport.Height / 2);
            _originalMouseState = Mouse.GetState();
            projection = Matrix.CreatePerspectiveFieldOfView(FieldOdView, AspectRatio, NearPlane, FarPlane);
            UpdateViewMatrix();
        }

        public void Update(GameTime gameTime)
        {
            MouseState currentMouseState = Mouse.GetState();
            KeyboardState keyState = Keyboard.GetState();

            if (currentMouseState != _originalMouseState)
            {
                float xDifference = currentMouseState.X - _originalMouseState.X;
                float yDifference = currentMouseState.Y - _originalMouseState.Y;
                _leftrightRot -= _rotationSpeed*xDifference;
                _updownRot -= _rotationSpeed*yDifference;
                Mouse.SetPosition(_width/2, _height/2);
                UpdateViewMatrix();
                currentMouseState = _originalMouseState;
            }

            if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
            {
                AddToCameraPosition(new Vector3(0, 0, -_sensibility));

            }
            if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S)) 
            {
                AddToCameraPosition(new Vector3(0, 0, _sensibility));

            }
            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D)) 
            {
                AddToCameraPosition(new Vector3(_sensibility, 0, 0));

            }
            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A)) 
            {
                AddToCameraPosition(new Vector3(-_sensibility, 0, 0));

            }
            if (keyState.IsKeyDown(Keys.Q)) 
            {
                AddToCameraPosition(new Vector3(0, _sensibility, 0));

            }
            if (keyState.IsKeyDown(Keys.Z)) 
            {
                AddToCameraPosition(new Vector3(0, -_sensibility, 0));

            }
        }

        public void GetCurrentCamera()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Tab))
            {
                cameraStateKeyDown = true;
            }
            else if (cameraStateKeyDown == true)
            {
                cameraStateKeyDown = false;
                _cameraState += 1;
                _cameraState %= 3;
            }

        }

        private void UpdateViewMatrix()
        {
            Matrix cameraRotation = Matrix.CreateRotationX(_updownRot) * Matrix.CreateRotationY(_leftrightRot);
            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            AvatarYaw = cameraRotatedTarget.X;
            _target = _position + cameraRotatedTarget;
            _up = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
            _view = Matrix.CreateLookAt(_position, _target, _up);
        }

        private void AddToCameraPosition(Vector3 vectorToAdd)
        {
            Matrix cameraRotation = Matrix.CreateRotationX(_updownRot) * Matrix.CreateRotationY(_leftrightRot);
            Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
            _position += _moveSpeed * rotatedVector;
            if (_cameraState == 2)
            {
                PlayerPositionToCamera(_position);
            }
            UpdateViewMatrix();
        }

        public void PlayerPositionToCamera(Vector3 position)
        {
            position.Z -= 500; 
            PlayerPosition = position;
        }

        public Matrix View
        {
            get { return _view; }
        }        

        public Matrix Projection
        {
            get { return projection; }
        }

        public float RotationSpeed
        {
            get { return _rotationSpeed; }
            set { _rotationSpeed = value; }
        }

        public float Sensibility
        {
            get { return _sensibility; }
            set { _sensibility = value; }
        }

        public float MoveSpeed
        {
            get { return _moveSpeed; }
            set { _moveSpeed = value; }
        }
    }
    }

